package com.sikaryofficial.file.controller;

import cn.hutool.core.text.CharSequenceUtil;
import com.sikaryofficial.common.core.domain.R;
import com.sikaryofficial.common.core.utils.file.FileUtils;
import com.sikaryofficial.common.redis.service.RedisService;
import com.sikaryofficial.file.config.FfmpegConfig;
import com.sikaryofficial.file.controller.mapping.FileMD5Mapping;
import com.sikaryofficial.file.service.ISysFileService;
import com.sikaryofficial.file.utils.FileMD5Utils;
import com.sikaryofficial.file.utils.FileUploadUtils;
import com.sikaryofficial.system.api.RemoteFileMD5Service;
import com.sikaryofficial.system.api.domain.SysFile;
import com.sikaryofficial.system.api.model.FileMd5VO;
import com.sikaryofficial.system.api.model.FileMergeReq;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.text.MessageFormat;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
 * 文件请求处理
 *
 * @author ruoyi
 */
@RestController
public class SysFileController {
    private static final Logger log = LoggerFactory.getLogger(SysFileController.class);
    private static final String FILE_MD5_CACHE_KEY = "hismk:file:md5:{0}";
    @Autowired
    private ISysFileService sysFileService;
    @Autowired
    private RedisService redisService;
    @Autowired(required = false)
    private RemoteFileMD5Service fileMD5Service;
    @Autowired
    private FfmpegConfig ffmpegConfig;

    /**
     * 文件上传请求
     */
    @PostMapping("/upload")
    public R<SysFile> upload(MultipartFile file) {
        return baseUpload(file);
    }

    /**
     * 文章附件文件上传请求
     */
    @PostMapping("/article/upload")
    public R<SysFile> articleUpload(MultipartFile file) {
        return baseUpload(file);
    }

    /**
     * 音频文件上传文件上传请求
     */
    @PostMapping("/audio/upload")
    public R<SysFile> audioUpload(MultipartFile file) {
        String md5 = FileMD5Utils.generateFileMd5(file);
        String cacheKey = buildCacheKey(md5);
        if (Boolean.TRUE.equals(redisService.hasKey(cacheKey))) {
            return R.ok(redisService.getCacheObject(cacheKey));
        }
        R<SysFile> fileMd5VOR = getSysFileR(md5, cacheKey);
        if (fileMd5VOR != null) {
            return fileMd5VOR;
        }
        try {
            // 上传并返回访问对象
            SysFile sysFile = sysFileService.uploadFileForAudio(file);
            saveFileMD5Info(md5, sysFile);
            redisService.setCacheObject(cacheKey, sysFile, 1L, TimeUnit.DAYS);
            return R.ok(sysFile);
        } catch (Exception e) {
            log.error("上传文件失败", e);
            return R.fail(e.getMessage());
        }
    }

    /**
     * 音视频文件合并请求
     */
    @PostMapping("/video/audioMerge")
    public R<SysFile> videoAndAudioMerge(
            @RequestParam("file") MultipartFile file,
            @RequestParam(value = "audioPath", required = false) String audioPath,
            @RequestParam(value = "audioStartTime", defaultValue = "0", required = false) String audioStartTime,
            @RequestParam(value = "audioDuration", defaultValue = "0", required = false) String audioDuration,
            @RequestParam(value = "videoCoverUrl", required = false) String videoCoverUrl
    ) {
        FileUploadUtils.verifyFileSizeMB(file, ffmpegConfig.getMaxLimitSize());
        log.info("audioDuration: {}" , audioDuration);
        if(CharSequenceUtil.isBlank(audioPath)){
            audioPath = "";
            audioStartTime = "0";
        }
        FileMergeReq req = FileMergeReq.builder()
                .videoFile(file)
                .videoPicStartTime("00:00:01")
                .audioPath(audioPath)
                .audioStartTime(Double.parseDouble(audioStartTime))
                .audioEndTime(Double.parseDouble(audioStartTime) + Double.parseDouble(audioDuration))
                .audioDuration(Double.parseDouble(audioDuration))
                .videoCoverUrl(videoCoverUrl)
                .build();
        log.info("req: {}" , req);
        String md5 = FileMD5Utils.generateMD5Hash(file, audioPath, audioStartTime, audioDuration, videoCoverUrl);
        String cacheKey = buildCacheKey(md5);
        if (Boolean.TRUE.equals(redisService.hasKey(cacheKey))) {
            return R.ok(redisService.getCacheObject(cacheKey));
        }
        R<SysFile> fileMd5VOR = getSysFileR(md5, cacheKey);
        if (fileMd5VOR != null) {
            return fileMd5VOR;
        }
        try {
            // 上传并返回访问对象
            SysFile sysFile = sysFileService.videoAndAudioMerge(req);
            // 替换视频封面图片
            if(CharSequenceUtil.isNotBlank(videoCoverUrl)){
                sysFile.setVideoCoverUrl(videoCoverUrl);
            }
            saveFileMD5Info(md5, sysFile);
            redisService.setCacheObject(cacheKey, sysFile, 1L, TimeUnit.DAYS);
            return R.ok(sysFile);
        } catch (Exception e) {
            log.error("上传文件失败", e);
            return R.ok(null,e.getMessage());
        }
    }

    /**
     * 文件上传请求
     */
    @PostMapping("/video/upload")
    public R<SysFile> uploadVideo(MultipartFile file) {
        FileUploadUtils.verifyFileSizeMB(file, ffmpegConfig.getMaxLimitSize());

        String md5 = FileMD5Utils.generateFileMd5(file);
        String cacheKey = buildCacheKey(md5);
        if (Boolean.TRUE.equals(redisService.hasKey(cacheKey))) {
            return R.ok(redisService.getCacheObject(cacheKey));
        }
        R<SysFile> fileMd5VOR = getSysFileR(md5, cacheKey);
        if (fileMd5VOR != null) {
            return fileMd5VOR;
        }
        try {
            // 上传并返回访问对象
            SysFile sysFile = sysFileService.uploadVideo(file);
            saveFileMD5Info(md5, sysFile);
            redisService.setCacheObject(cacheKey, sysFile, 1L, TimeUnit.DAYS);
            return R.ok(sysFile);
        } catch (Exception e) {
            log.error("上传文件失败", e);
            return R.ok(null,e.getMessage());
        }
    }

    private String buildCacheKey(String md5) {
        return MessageFormat.format(FILE_MD5_CACHE_KEY, md5);
    }

    private void saveFileMD5Info(String md5, SysFile sysFile) {
        if (Objects.isNull(fileMD5Service)) {
            return;
        }
        FileMd5VO fileMd5 = FileMD5Mapping.INSTANCE.coverToVO(sysFile);
        fileMd5.setMd5(md5);
        try {
            fileMD5Service.add(fileMd5);
        } catch (Exception e) {
            log.error("保存文件失败", e);
        }
    }

    private R<SysFile> baseUpload(MultipartFile file) {
        String md5 = FileMD5Utils.generateFileMd5(file);
        String cacheKey = buildCacheKey(md5);
        if (Boolean.TRUE.equals(redisService.hasKey(cacheKey))) {
            return R.ok(redisService.getCacheObject(cacheKey));
        }
        R<SysFile> fileMd5VOR = getSysFileR(md5, cacheKey);
        if (fileMd5VOR != null) {
            return fileMd5VOR;
        }
        try {
            // 上传并返回访问地址
            String url = sysFileService.uploadFile(file);
            SysFile sysFile = new SysFile();
            sysFile.setName(FileUtils.getName(url));
            sysFile.setUrl(url);
            // 保存上传文件信息到数据库
            saveFileMD5Info(md5, sysFile);
            redisService.setCacheObject(cacheKey, sysFile, 1L, TimeUnit.DAYS);
            return R.ok(sysFile);
        } catch (Exception e) {
            log.error("上传文件失败", e);
            return R.ok(null,e.getMessage());
        }
    }

    private R<SysFile> getSysFileR(String md5, String cacheKey) {
        if (Objects.isNull(fileMD5Service)) {
            return null;
        }
        try {
            R<SysFile> fileMd5VOR = fileMD5Service.getInfo(md5);
            if (Objects.nonNull(fileMd5VOR) && Objects.nonNull(fileMd5VOR.getData())) {
                redisService.setCacheObject(cacheKey, fileMd5VOR.getData(), 1L, TimeUnit.DAYS);
                return R.ok(fileMd5VOR.getData());
            }
        } catch (Exception e) {
            log.error("获取文件失败", e);
        }
        return null;
    }
}